// MainActivity.java
package com.mytech.cslive.activities;

import java.math.BigInteger;
import java.net.BindException;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;

import jim.h.common.android.zxinglib.integrator.IntentIntegrator;
import jim.h.common.android.zxinglib.integrator.IntentResult;

import com.mytech.cslive.Player;
import com.mytech.cslive.PlayerStatus;
import com.mytech.cslive.PromptDialog;
import com.mytech.cslive.PromptDialog.SecuredMethodListener;
import com.mytech.cslive.R;
import com.mytech.cslive.Player.ShowPlayerMessagesListener;
import com.mytech.cslive.Player.ShowPlayerStatusListener;
import com.mytech.cslive.Team;
import com.mytech.cslive.infrastructure.AppConfig;
import com.mytech.cslive.services.LaserKitService;
import com.mytech.cslive.services.LocationStatusService;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Messenger;
import android.os.StrictMode;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.Spinner;
import android.widget.Toast;
import android.widget.ToggleButton;

public class MainActivity extends Activity implements OnClickListener,
		ShowPlayerStatusListener, ShowPlayerMessagesListener,
		SecuredMethodListener {

	public static final String PROPERTY_QRCODE_FLAG = "QRFLAG";
	public static final String PROPERTY_PASSWORD = "CSLIVE_PASSWORD";

	public static final String PROPERTY_PLAYER = "Player";
	private static final String TAG = "CSLive/MainActivity";
	private Player player;

	private boolean serviceIsOn;
	private boolean buttonEnabled;


	// List for views to enable/disable
	private ArrayList<View> lockableViews;

	private static final String QRCODE_HASH = "c5836008c1649301e29351a55db8f65c";
	private static final String PASSWORD_HASH = "0cc175b9c0f1b6a831c399e269772661";

	// Handles connection with services
	private Handler locationChangedHandler;
	private Handler gotHitHandler;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		// Set context to app config
		AppConfig.setMainContext(this);

		this.setLockableViews(new ArrayList<View>());

		// Save player on the intent helper so that the settings activity
		// can access it as well
		this.setButtonsListener();

		this.initializeTeamSpinner();
		this.initializeIdSpinner();
		this.initializeStatusSpinner();

		// Setting services to off
		this.serviceIsOn = false;
		buttonEnabled = true;
		
		// TODO Change udp connection handler send to async task ?
		// Note: Change is probably not needed
		StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
				.permitAll().build();
		StrictMode.setThreadPolicy(policy);

		player = new Player();

		// Set listener to player status
		player.setShowPlayerStatusListener(this);

		// Set listener to player messages
		player.setShowPlayerMessagesListener(this);

		// Handles connection with services
		locationChangedHandler = player.getLocationChangedHandler();
		gotHitHandler = player.getGotHitHandler();

		// Set player to intent helper
		IntentHelper.addObjectForKey(PROPERTY_PLAYER, player);

		// Setting properties
		AppConfig.setProperty(PROPERTY_QRCODE_FLAG, QRCODE_HASH);
		AppConfig.setProperty(PROPERTY_PASSWORD, PASSWORD_HASH);

	}

	
	/**
	 * Generating an MD5 hash from a given String
	 * 
	 * @param s
	 *            The given String to generate hash from
	 * @return MD5 hash of the String
	 */
	public static String md5(String s) {
		MessageDigest digest;
		String hash = null;
		try {
			digest = MessageDigest.getInstance("MD5");
			byte utf8_bytes[] = s.getBytes();
			digest.update(utf8_bytes, 0, utf8_bytes.length);
			hash = new BigInteger(1, digest.digest()).toString(16);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return hash;
	}
	public static String getPropertyPlayer() {
		return PROPERTY_PLAYER;
	}

	public boolean isServiceIsOn() {
		return serviceIsOn;
	}

	public void setServiceIsOn(boolean serviceIsOn) {
		this.serviceIsOn = serviceIsOn;
	}

	public Player getPlayer() {
		return player;
	}

	public Handler getLocationChangedHandler() {
		return locationChangedHandler;
	}

	public void setLocationChangedHandler(Handler locationChangedHandler) {
		this.locationChangedHandler = locationChangedHandler;
	}

	public Handler getGotHitHandler() {
		return gotHitHandler;
	}

	public void setGotHitHandler(Handler gotHitHandler) {
		this.gotHitHandler = gotHitHandler;
	}

	private void setButtonsListener() {
		Button engageButton = (Button) findViewById(R.id.engage_button);
		engageButton.setOnClickListener(this);
		this.getLockableViews().add(engageButton);

		ImageButton settingsButton = (ImageButton) findViewById(R.id.settings_button);
		settingsButton.setOnClickListener(this);
		this.getLockableViews().add(settingsButton);

		// Don't add the scan button to lock list
		ImageButton scanButton = (ImageButton) findViewById(R.id.scan_button);
		scanButton.setOnClickListener(this);

		// Don't add the lock button to lock list
		ImageButton lockButton = (ImageButton) findViewById(R.id.lock_button);
		lockButton.setOnClickListener(this);
	}

	private void initializeTeamSpinner() {
		int spinnerId = R.id.spinner_team;

		initializeSpinner(spinnerId, Team.getTeamValues());

		Spinner spinner = (Spinner) findViewById(spinnerId);
		spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
			@Override
			public void onItemSelected(AdapterView<?> parent, View view,
					int pos, long id) {
				String teamString = (String) parent.getItemAtPosition(pos);
				Team team = Team.parseString(teamString);
				getPlayer().setTeam(team);
			}

			@Override
			public void onNothingSelected(AdapterView<?> parent) {
				// TODO Auto-generated method stub
			}
		});
	}

	private void initializeIdSpinner() {
		int spinnerId = R.id.spinner_id;

		initializeSpinner(spinnerId, Player.getIdValues());

		Spinner spinner = (Spinner) findViewById(spinnerId);
		spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
			@Override
			public void onItemSelected(AdapterView<?> parent, View view,
					int pos, long id) {
				String idString = (String) parent.getItemAtPosition(pos);
				int playerId = Integer.parseInt(idString);
				getPlayer().setId(playerId);
			}

			@Override
			public void onNothingSelected(AdapterView<?> parent) {
				// TODO Auto-generated method stub
			}
		});
	}

	private void initializeStatusSpinner() {
		int spinnerId = R.id.spinner_status;

		initializeSpinner(spinnerId, PlayerStatus.getStatusValues());

		Spinner spinner = (Spinner) findViewById(spinnerId);
		spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
			@Override
			public void onItemSelected(AdapterView<?> parent, View view,
					int pos, long id) {
				String statusString = (String) parent.getItemAtPosition(pos);
				PlayerStatus playerStatus = PlayerStatus
						.parseString(statusString);

				getPlayer().setStatus(playerStatus);
			}

			@Override
			public void onNothingSelected(AdapterView<?> parent) {
				// TODO Auto-generated method stub
			}
		});
	}

	/**
	 * Initializing a generic spinner
	 * 
	 * @param spinnerId
	 *            The spinner layout id
	 * @param spinnerValues
	 *            The values of the spinner, String array
	 */
	private void initializeSpinner(int spinnerId, String[] spinnerValues) {
		Spinner spinner = (Spinner) findViewById(spinnerId);

		// Create an ArrayAdapter using the string array and a default spinner
		// layout
		ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
				android.R.layout.simple_spinner_item, spinnerValues);

		// Specify the layout to use when the list of choices appears
		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

		// Apply the adapter to the spinner
		spinner.setAdapter(adapter);
		
		// Adding spinner to lockable view
		this.getLockableViews().add(spinner);
	}

	/**
	 * Toggling application services
	 */
	private void toggleServices() {
		if (isServiceIsOn()) {
			this.shutdownServices();
			this.setServiceIsOn(false);
		} else {
			this.startServices();
			this.setServiceIsOn(true);
		}
	}

	/**
	 * Starting application services
	 */
	private void startServices() {
		// Starting services
		// TODO remove dbg
		Log.d(TAG, "Initializing service");

		try {
			// Activate player to start sending data
			this.getPlayer().activate();

			this.startLaserKitService();
			this.startLocationService();
		} catch (BindException e) {
			this.toggleEngageButton();

			// Show a message to the user
			Toast.makeText(this, "IP invalid!", Toast.LENGTH_SHORT).show();
		} catch (UnknownHostException e) {
			this.toggleEngageButton();

			// Show a message to the user
			Toast.makeText(this, "Unknown IP host!", Toast.LENGTH_SHORT).show();
		} catch (SocketException e) {
			this.toggleEngageButton();

			// Show a message to the user
			Toast.makeText(this, "Socket invalid!", Toast.LENGTH_SHORT).show();
		} catch (Exception ex) {
			this.toggleEngageButton();

			Toast.makeText(this,
					ex.getClass().toString() + " " + ex.getMessage(),
					Toast.LENGTH_LONG).show();
		}
	}

	private void toggleEngageButton() {
		ToggleButton engageButton = (ToggleButton) findViewById(R.id.engage_button);
		engageButton.toggle();
	}

	private void startLaserKitService() {
		Intent intent = new Intent(MainActivity.this, LaserKitService.class);
		intent.putExtra(LaserKitService.EXTRA_MESSENGER, new Messenger(
				getGotHitHandler()));
		startService(intent);
	}

	private void shutdownLaserKitService() {
		stopService(new Intent(MainActivity.this, LaserKitService.class));
	}

	private void startLocationService() {
		startLocationService(MainActivity.this);
	}

	private void shutdownLocationService() {
		shutdownLocationService(MainActivity.this);
	}

	public static void startLocationService(Activity activity) {
		// Location Service
		Intent intent = new Intent(activity, LocationStatusService.class);
		Player player = (Player) IntentHelper.getObjectForKey(PROPERTY_PLAYER);

		intent.putExtra(LocationStatusService.EXTRA_MESSENGER, new Messenger(
				player.getLocationChangedHandler()));
		activity.startService(intent);
	}

	public static void shutdownLocationService(Activity activity) {
		activity.stopService(new Intent(activity, LocationStatusService.class));
	}

	/**
	 * Shutting application services down
	 */
	private void shutdownServices() {
		// Stopping services
		this.shutdownLaserKitService();
		this.shutdownLocationService();
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();

		// TODO Might want to add onPause, or onStop
		this.shutdownServices();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.activity_main, menu);
		return true;
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.engage_button:
			toggleServices();

			break;
		case R.id.settings_button:
			// Define a new intent for the settings activity
			Intent intent = new Intent(this, SettingsActivity.class);

			// Start the activity
			this.startActivity(intent);

			break;
			
		case R.id.scan_button:
			// Make sure player is alive before scan
			if (getPlayer().getStatus().equals(PlayerStatus.ALIVE)) {
				// Start scan activity
				IntentIntegrator.initiateScan(MainActivity.this, R.layout.capture,
						R.id.viewfinder_view, R.id.preview_view, true);
			} else if (getPlayer().getStatus().equals(PlayerStatus.DEAD)) {
				this.showPlayerMessage("A dead player cannot take the flag!");
			} else if (getPlayer().getStatus().equals(PlayerStatus.HAS_FLAG)) {
				this.showPlayerMessage("You already have the flag!");
			} else {
				this.showPlayerMessage("Player status must be \'ALIVE\' to take the flag!");
			}
			break;
		case R.id.lock_button:
			promptPassword();
			
			break;
		default:
			Log.d(TAG, "No such button, what the hell did you do?");

			break;
		}
	}

	private void selectSpinnerItem(String value, Spinner spinner) {
		ArrayAdapter myAdapter = (ArrayAdapter) spinner.getAdapter();

		// Get value position in spinner
		int spinnerPosition = myAdapter.getPosition(value);

		// Set the selection according to value
		spinner.setSelection(spinnerPosition);
	}

	@Override
	public void showPlayerStatus(PlayerStatus status) {
		Spinner playerStatusSpinner = (Spinner) findViewById(R.id.spinner_status);

		this.selectSpinnerItem(status.toPresentableString(),
				playerStatusSpinner);
	}

	@Override
	public void showPlayerMessage(String message) {
		this.showPlayerMessage(message, false);
	}

	@Override
	public void showPlayerMessage(String message, boolean longViewLength) {
		int length = Toast.LENGTH_SHORT;

		if (longViewLength) {
			length = Toast.LENGTH_LONG;
		}

		Toast.makeText(this, message, length).show();
	}
	
	@Override
	public void handleCorrectPassword() {
		toggleButtons(!isButtonEnabled());
	}
	
	private boolean isButtonEnabled() {
		return buttonEnabled;
	}

	private ArrayList<View> getLockableViews() {
		return lockableViews;
	}
	
	private void promptPassword() {
		PromptDialog dlg = new PromptDialog(MainActivity.this,
				R.string.password_title, R.string.password_dialog_text, this) {
			@Override
			public boolean onOkClicked(String input, SecuredMethodListener sml) {
				String hashedResult = MainActivity.md5(input);
				String password = AppConfig
						.getProperty(MainActivity.PROPERTY_PASSWORD);

				if (hashedResult.equals(password.replaceFirst("^0+(?!$)", ""))) {
					sml.handleCorrectPassword();
					
					return true;
				} else {
					return false;
				}
			}
		};
		dlg.show();
	}

	private void setButtonEnabled(boolean buttonEnabled) {
		this.buttonEnabled = buttonEnabled;
	}
	
	private void setLockableViews(ArrayList<View> lockableViews) {
		this.lockableViews = lockableViews;
	}
	
	
	/**
	 * Toggle the buttons state
	 * 
	 * @param flag
	 *            True for enabling, false for disabling
	 */
	private void toggleButtons(boolean flag) {
		for (View v : this.getLockableViews()) {
			v.setEnabled(flag);
		}
		
		setButtonEnabled(flag);

		// Setting icon
		ImageButton lockButton = (ImageButton) findViewById(R.id.lock_button);

		if (flag == true) {
			lockButton.setImageResource(R.drawable.stock_lock_open);
		} else {
			lockButton.setImageResource(R.drawable.stock_lock);
		}

	}
	
	
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		switch (requestCode) {
			case IntentIntegrator.REQUEST_CODE:
				IntentResult scanResult = IntentIntegrator.parseActivityResult(
						requestCode, resultCode, data);
				if (scanResult == null) {
					return;
				}
				
				final String result = scanResult.getContents();
				
				if (result != null) {
					// Calculating hash
					String hashedResult = MainActivity.md5(result);
	
					String password = AppConfig
							.getProperty(MainActivity.PROPERTY_QRCODE_FLAG);
	
					if (hashedResult.equals(password.replaceFirst("^0+(?!$)", ""))) {
						// The QRCODE is true
						Toast.makeText(this, "Got flag, now run!!!",
								Toast.LENGTH_SHORT).show();
						getPlayer().setStatus(PlayerStatus.HAS_FLAG);
	
					} else {
						// Wrong code was scanned
						Toast.makeText(this, "Wrong code was scanned!!!",
								Toast.LENGTH_SHORT).show();
					}
				} else {
	
				}
			break;
		default:
			break;
		}
	}

}
